/*
 * Copyright © 2018 www.noark.xyz All Rights Reserved.
 *
 * 感谢您选择Noark框架，希望我们的努力能为您提供一个简单、易用、稳定的服务器端框架 ！
 * 除非符合Noark许可协议，否则不得使用该文件，您可以下载许可协议文件：
 *
 *        http://www.noark.xyz/LICENSE
 *
 * 1.未经许可，任何公司及个人不得以任何方式或理由对本框架进行修改、使用和传播;
 * 2.禁止在本项目或任何子项目的基础上发展任何派生版本、修改版本或第三方版本;
 * 3.无论你对源代码做出任何修改和改进，版权都归Noark研发团队所有，我们保留所有权利;
 * 4.凡侵犯Noark版权等知识产权的，必依法追究其法律责任，特此郑重法律声明！
 */
package xyz.noark.codec.bp;


import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONB;
import com.alibaba.fastjson2.JSONWriter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zfoo.protocol.ProtocolManager;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.UnpooledHeapByteBuf;
import org.apache.fury.Fury;
import org.apache.fury.config.Language;
import org.msgpack.jackson.dataformat.MessagePackFactory;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.results.format.ResultFormatType;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import xyz.noark.codec.bp.proto.FightReq;
import xyz.noark.codec.bp.proto.req.*;

import java.io.IOException;
import java.io.InputStream;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 序列化反序列化性能大比拼。
 * <p>
 * JMH Visualizer: <a href="https://jmh.morethan.io/">JMH Visualizer</a>
 *
 * @author 小流氓[176543888@qq.com]
 */
@Fork(1)
@Warmup(iterations = 10, time = 1)
@Measurement(iterations = 10, time = 1)
@OutputTimeUnit(TimeUnit.SECONDS)
@BenchmarkMode({Mode.Throughput})
@State(Scope.Benchmark)
public class EncodeBenchmark {
    private FightReq req;
    private ObjectMapper mapper;
    private Fury fury;
    private Fury furyX;
    private ByteBuf buffer;

    // 使用@Setup注解来初始化状态对象
    @Setup(Level.Trial)
    public void init() throws IOException {
        ClassLoader classLoader = EncodeBenchmark.class.getClassLoader();
        try (InputStream is = classLoader.getResourceAsStream("proto/fight.req.json")) {
            req = JSON.parseObject(is, FightReq.class);
        }

        {// FastJson2
            byte[] bytes = JSONB.toBytes(req);
            System.out.println();
            System.out.println("fastjson2 len=" + bytes.length);
            System.out.println();
        }

        {// MsgPack
            this.mapper = new ObjectMapper(new MessagePackFactory());
            byte[] bytes = mapper.writeValueAsBytes(req);
            System.out.println();
            System.out.println("msgpack len=" + bytes.length);
            System.out.println();
        }
        Set<Class<?>> classes = Set.of(FightReq.class, FightArmsDTO.class, FightHeroDTO.class,
                FightSkillExtendDTO.class, FightLocationDTO.class, FightNpcDTO.class, FightNpcHeroDTO.class,
                FightNpcTroopsDTO.class, FightPlayDTO.class, FightPlayerDTO.class, FightPlayerTroopsDTO.class,
                FightSkillDTO.class, FightTroopsBuffDTO.class,
                FightTroopsDTO.class, ShowData.class, TechCondition.class, TechInfo.class, TechEffect.class
        );
        {// Fury
            fury = Fury.builder().withLanguage(Language.JAVA)
                    .withRefTracking(true).requireClassRegistration(false).build();

            furyX = Fury.builder().withLanguage(Language.JAVA)
                    .withRefTracking(false).requireClassRegistration(true).withNumberCompressed(true).build();
            for (Class<?> aClass : classes) {
                furyX.register(aClass);
            }

            {
                byte[] bytes = fury.serializeJavaObject(req);
                System.out.println();
                System.out.println("fury len=" + bytes.length);
                System.out.println();
            }
            {
                byte[] bytes = furyX.serializeJavaObject(req);
                System.out.println();
                System.out.println("furyX len=" + bytes.length);
                System.out.println();
            }
        }

        { // Zfoo
            // netty的ByteBuf做了更多的安全检测，java自带的ByteBuffer并没有做安全检测，为了公平，把不需要的检测去掉
            // java通过ByteBuffer.allocate(1024 * 8)构造出来的是使用了unsafe的HeapByteBuffer，为了公平，使用netty中带有unsafe操作的UnpooledUnsafeHeapByteBuf
            System.setProperty("io.netty.buffer.checkAccessible", "false");
            System.setProperty("io.netty.buffer.checkBounds", "false");

            this.buffer = new UnpooledHeapByteBuf(ByteBufAllocator.DEFAULT, 100, 1_0000);

            // zfoo协议注册，只能初始化一次
            ProtocolManager.initProtocol(Set.of(FightReq.class, FightArmsDTO.class, FightHeroDTO.class,
                    FightSkillExtendDTO.class, FightLocationDTO.class, FightNpcDTO.class, FightNpcHeroDTO.class,
                    FightNpcTroopsDTO.class, FightPlayDTO.class, FightPlayerDTO.class, FightPlayerTroopsDTO.class,
                    FightSkillDTO.class, FightTroopsBuffDTO.class,
                    FightTroopsDTO.class, ShowData.class, TechCondition.class, TechInfo.class, TechEffect.class
            ));


            buffer.clear();
            ProtocolManager.write(buffer, req);
            byte[] bytes = buffer.array();
            System.out.println();
            System.out.println("zfoo len=" + bytes.length);
            System.out.println();
        }
    }

    @Benchmark
    public void fastJson2Serialize(Blackhole bh) {
        bh.consume(JSONB.toBytes(req));
    }

    @Benchmark
    public void fastJson2SerializeWithBeanToArray(Blackhole bh) {
        bh.consume(JSONB.toBytes(req, JSONWriter.Feature.BeanToArray));
    }

    @Benchmark
    public void fastJson2SerializeWithBeanToArrayAndFieldBase(Blackhole bh) {
        bh.consume(JSONB.toBytes(req, JSONWriter.Feature.BeanToArray, JSONWriter.Feature.FieldBased));
    }


    @Benchmark
    public void furySerialize(Blackhole bh) {
        bh.consume(fury.serializeJavaObject(req));
    }

    @Benchmark
    public void furySerializeWithClassRegistrationAndNumberCompressed(Blackhole bh) {
        bh.consume(furyX.serializeJavaObject(req));
    }

    @Benchmark
    public void msgpack(Blackhole bh) throws IOException {
        bh.consume(mapper.writeValueAsBytes(req));
    }

    @Benchmark
    public void zfoo(Blackhole bh) {
        buffer.clear();
        ProtocolManager.write(buffer, req);
        bh.consume(buffer.array());
    }

    public static void main(String[] args) throws Exception {
        Options options = new OptionsBuilder()
                .include(EncodeBenchmark.class.getName())
                .resultFormat(ResultFormatType.JSON).result("result.json")
                .build();
        new Runner(options).run();
    }
}